package pr3.cromosoma;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import pr3.funcion.DU;
import pr3.funcion.EQ;

import pr3.funcion.Funcion;
import pr3.funcion.MM;
import pr3.funcion.MP;
import pr3.funcion.NOT;

public class Utilidades {
    
    private static final List<Class> funcionesHoja = Arrays.asList(new Class[] {
//        BS.class,
//        CP.class, 
        MM.class, 
        MP.class, 
//        SN.class
    });
    private static final List<Class> funcionesNoHoja = Arrays.asList(new Class[] {
        DU.class, 
        EQ.class, 
        NOT.class
    });
    private static final List<Class> funciones = Arrays.asList(new Class[] {
//        BS.class, 
//        CP.class, 
        MM.class, 
        MP.class, 
//        SN.class,
        DU.class, 
        EQ.class, 
        NOT.class
    });
    private static final Map<Integer, List<Class>> funcionesPorAridad;
    
    static {
        funcionesPorAridad = new HashMap<Integer, List<Class>>(3);
        funcionesPorAridad.put(0, funcionesHoja);
        funcionesPorAridad.put(1, Arrays.asList(new Class[] {NOT.class}));
        funcionesPorAridad.put(2, Arrays.asList(new Class[] {EQ.class, DU.class}));
    }

	public static ArrayList<Estado> carga() {

		ArrayList<Estado> estados = new ArrayList<Estado>();

		try {
			BufferedReader br = new BufferedReader(new FileReader(new File(
					"planificacion.txt")));

			String linea = br.readLine();

			while (linea != null) {

				Estado estado = new Estado();
				StringTokenizer stringTokenizer = new StringTokenizer(linea);
				String nextChar = stringTokenizer.nextToken();

				while (!nextChar.equals("*")) {
					estado.getPila().add(nextChar.charAt(0));
					nextChar = stringTokenizer.nextToken();
				}

				nextChar = stringTokenizer.nextToken();

				while (!nextChar.equals("*")) {
					estado.getMesa().add(nextChar.charAt(0));
					nextChar = stringTokenizer.nextToken();
				}

                estado.calcularTerminales();
				estados.add(estado);
				linea = br.readLine();
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return estados;
	}

	public static Funcion generarArbol(int profRestante, int profMin) {
		boolean quieroHojas, quieroNoHojas;
        if(profRestante <= 1) {
            quieroHojas = true;
            quieroNoHojas = false;            
        }
        else if(profRestante > profMin) { //queremos mayor profundidad  => no queremos hojas
            quieroHojas = false;
            quieroNoHojas = true;
        }
        else { //queremos cualquier cosa
            quieroHojas = true;
            quieroNoHojas = true;
        }
        Funcion resultado = generarFuncion(quieroHojas, quieroNoHojas);
        for(int i = 0; i < resultado.getAridad(); i++) {
            resultado.setArgumento(i, generarArbol(profRestante - 1, profMin));
            resultado.getArgumento(i).setPadre(resultado);
        }
        return resultado;
	}

    public static Funcion generarFuncion(boolean quieroHojas, boolean quieroNoHojas) {
        if(!quieroHojas && !quieroNoHojas) {
            throw new IllegalArgumentException("Conjunto vacío: Al menos uno de los dos "
                    + "parámetros debe ser cierto");
        }
        List<Class> conjunto;
        if(quieroHojas && !quieroNoHojas) {
            conjunto = funcionesHoja;
        }
        else if(!quieroHojas && quieroNoHojas) {
            conjunto = funcionesNoHoja;
        }
        else {
            conjunto = funciones;
        }
        return generarFuncion(conjunto);
    }
    
    public static Funcion generarFuncion(int aridad) {
        return generarFuncion(funcionesPorAridad.get(aridad));
    }
    
    public static Funcion generarFuncion(List<Class> candidatos) {
        Class c = candidatos.get((int)(Math.random() * candidatos.size())); 
        try {
            return (Funcion)c.getConstructor().newInstance();
        }
        catch (Exception ex) {
            throw new RuntimeException("Error al instanciar una funcion", ex);
        }
    }
    
    public static Funcion getNodoAleatorio(CromosomaArbol c) {
        return getNodoAleatorio(c, Integer.MAX_VALUE);
    }
    
    public static Funcion getNodoAleatorio(CromosomaArbol c, int profMax) {
        int numNodos = c.getNumNodos(profMax);
        int numAleatorio = (int) (numNodos * Math.random());
        numAleatorio++; //esto es para no seleccionar el elemento raiz, que no es valido
        CromosomaIterator it = new CromosomaIterator(c, profMax);
        while(it.hasNext()) {
            Funcion f = it.next();
            if(numAleatorio > 0)
                numAleatorio--;
            else {
                assert(f.getPadre() != null);
                return f;
            }
        }
        assert(false);
        return null;
    }
}
